#include <avr/io.h>

#define F_CPU 8000000

#include <util/delay.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include "analog_comparator.h"
#include "fixed_point.h"

#define ACMUX _SFR_MEM8(0x7D)
#define AC_TIME_SCALE 8000000.
#define TIME_CONSTANT 0.01
#define V_BAT 0x51C28
//5.11

volatile char AC_FLAG = 0;
unsigned long ac_global_var;
double time_stuff = 0;
double sensor_offset[5];
double sensor_slope[5];

double EEMEM eeprom_offset[5];
double EEMEM eeprom_slope[5];

/*
int something(void) {

	double val;
	
	init_serial();
	ADC_init();

	FILE s=FDEV_SETUP_STREAM(serial_putch, NULL, _FDEV_SETUP_WRITE);
	stdout=&s;

	while (1) {
		start_conversion();
		while(!AC_FLAG);
		val = 5.1 * (1 - exp(-(((double)AC_GLOBAL_VAR)/TIME_STUFF)));
		printf("%u\n\r", AC_GLOBAL_VAR);
		print_float((float)val);
		printf("\n\r");
		PORTC &= ~(1<<PORTC5);
		_delay_ms(500);
	}
	return 0;
}
*/
void send_int(unsigned long val);

double AC_conversion(void) {
	double val, val1, val2, average_val;
	unsigned long tmp1, tmp2;

	PORTB &= ~(1<<PORTB5);

	_delay_ms(20);
	start_conversion();
	while(!AC_FLAG);
//	val =  4.98 * (1 - exp(-(((double)ac_global_var)/(time_stuff/(0.248461359)) )));
	val =  5 * (1 - exp(-(((double)ac_global_var)/(time_stuff) )));

/*	_delay_ms(20);
	start_conversion_charging();
	while(!AC_FLAG);
	tmp1 = ac_global_var;

	PORTB |= (1<<PORTB5);
	_delay_ms(20);
	start_conversion_discharging();
	while(!AC_FLAG);
	tmp2 = ac_global_var;

	printf("tmp1 = %d\t tmp2 = %d\t", tmp1, tmp2);
	//average_val=tmp2;

	val1 =  4.98 * (1 - exp(-((tmp1)/(time_stuff/(0.248461359)) )));
	val2 =  4.98 * (exp(-((tmp2)/(time_stuff/(0.248461359)) )));

	printf("val1 = ");
	print_double(val1);
	printf("\t");

	printf("val2 = ");
	print_double(val2);
	printf("\n");
		
	val = (val1 + val2) / 2;
*/

	PORTB &= ~(1<<PORTB5);
	return val;
}

void ADC_init(void) {

	int i;

	for (i = 0; i < 5; i++) {
		if (eeprom_read_byte(&eeprom_offset[i]) != 0xff) {
			eeprom_read_block((void*)&sensor_offset[i], (const void*)&eeprom_offset[i], sizeof(double));
		} else {
			sensor_offset[i] = 0.47;
		}

		if (eeprom_read_byte(&eeprom_slope[i]) != 0xff) {
			eeprom_read_block((void*)&sensor_slope[i], (const void*)&eeprom_slope[i], sizeof(double));
		} else {
			sensor_slope[i] = .0195;
		}
	}

	DDRB |= (1<<DDB5);
	TCCR1B |= (1<<CS10);
//	DDRD &= ~(1<<PORTD1) & ~(1<<PORTD3);
	DDRD = 0;  //FIXME may need to change to enable serial
	DIDR1 = 0xFF; //FIXME disable digital input, may need to change to enable serial
//	time_stuff = 10000 / 0.248461359;


	ACMUX = 1;
	ACSR |= (1 << ACBG);


	start_conversion();
	while(!AC_FLAG);
	//printf("%d\n", ac_global_var);
	// to calibrate, we measure the 1.1v source 
//with 1.1=5-5exp(-t/rc) we get a t that gives 1.1 v
// solving for RC gives measured/0.248461359 as actual RC assuming 5 v vcc 
//	printf("Initialization AC_GLOBAL_VAR = %u\n", ac_global_var);
//	time_stuff = ac_global_var;
	time_stuff = (double)ac_global_var / 0.248461359;
	double val=  5 * (1 - exp(-(ac_global_var/time_stuff))); 
	// some number forrest doesn't want to lose 31393 )));
//	printf("Initialization voltage = ");
//	print_double(val);
//	printf("\n");
	//time_stuff = ((double)ac_global_var);///0.242408163;
	//time_stuff = Q_DIV(((unsigned long)0x3f76), ac_global_var);
	ACSR &= ~(1 << ACBG);
	

	ACMUX = 2;
}

void start_conversion(void) {
	PORTB &= ~(1<<PORTB5);//discharge cap
	PORTD = 0;
	_delay_ms(20);
	AC_FLAG = 0;
	sei();
	ACSR &= ~((1<<ACIS1) | (1<<ACIS0));  // interrupt on toggle
	ACSR |= (1<<ACI);		// clears interrupt flag
	ACSR |= (1<<ACIE);		// enable interrupt
	TCNT1 = 0;
	PORTB |= (1<<PORTB5);  // turn on the cap
}

void start_conversion_charging(void) {
	AC_FLAG = 0;
	sei();
	ACSR &= ~((1<<ACIS1) | (1<<ACIS0));
	ACSR |= (1<<ACI);		// clears interrupt flag
	ACSR |= (1<<ACIE);		// enable interrupt
	TCNT1 = 0;
	PORTB |= (1<<PORTB5);  // turn on the cap
}

void start_conversion_discharging(void) {
	AC_FLAG = 0;
	sei();
	ACSR &= ~(1<<ACIS0);
	ACSR |= (1<<ACIS1);
	ACSR |= (1<<ACI);		// clears interrupt flag
	ACSR |= (1<<ACIE);		// enable interrupt
	TCNT1 = 0;
	PORTB &= ~(1<<PORTB5);  // turn off the cap
}

/*
double full_conversion(double batt_volt) {
		double val1, val2;
		float scale = (float)AC_TIME_SCALE*TIME_CONSTANT;
		start_conversion();
	    while(!AC_FLAG);
	//	val1 = batt_volt * (1 - exp(-(AC_GLOBAL_VAR/(double)AC_TIME_SCALE)/TIME_CONSTANT));
		val1 =  batt_volt * (1 - exp(-(AC_GLOBAL_VAR/scale)));
//		val1 = batt_volt * (1 - exp(-AC_GLOBAL_VAR/scale));
		_delay_ms(5);
		AC_FLAG = 0;
		sei();
		ACSR |= (1<<ACIS1) | (1<<ACIS0);
        ACSR |= (1<<ACI);               // clears interrupt flag
        ACSR |= (1<<ACIE);              // enable interrupt
        TCNT1 = 0;
		PORTC &= ~(1<<PORTC5);  // turn off the cap
		while(!AC_FLAG);
	//	val2 = batt_volt * exp(-(AC_GLOBAL_VAR/(double)AC_TIME_SCALE)/TIME_CONSTANT);
		val2 = batt_volt * exp(-AC_GLOBAL_VAR/TIME_CONSTANT/AC_TIME_SCALE);
		return ((val1 + val2) / 2);
}
*/

ISR(ANALOG_COMP_vect) {
	ac_global_var = TCNT1;	// save timer in global 
	AC_FLAG = 1;			// set complete flag
	ACSR &= ~(1<<ACIE);		// disable interrupt
	PORTB &= ~(1<<PORTB5);  // turn off the cap
}

int print_double(double f)
{
    int c;
    double fi;

    f=modf(f,&fi);
    c=printf("%d.",(int)fi);
    modf(fabs(f*1000),&fi);
    c+=printf("%03d",(int)fi);
    return c;
}


unsigned long q_nexp(unsigned long x)
{
	unsigned long num;
	unsigned long den;
	long value;
	char i,sign;

	sign = -1;
	num = den = value = (QONE);

	for (i = 1; i < 10; i++)
	{
		den *= i;
		num = Q_MULT(num, x);

		if (sign < 0) value -= Q_DIV(num, den);
		else value += Q_DIV(num, den);

		sign = -sign;
	}

	return value;
}

unsigned long get_global_var(void) {
	return ac_global_var;
}

void select_AIN_pin(int x) {
	if (x == 0) {
		ACMUX = 1;
		ACSR |= (1 << ACBG);
	}
	else if (x == 1) {
		ACMUX = 2;
		ACSR &= ~(1 << ACBG);
	}
	else if (x == 2) { 
		ACMUX = 3;
		ACSR &= ~(1 << ACBG);
	}
	else if (x == 3) { 
		ACMUX = 4;
		ACSR &= ~(1 << ACBG);
	}
	else { 
		ACMUX = 5;
		ACSR &= ~(1 << ACBG);
	}
	
}

void set_time_stuff(unsigned long x) {
	time_stuff = (double)x / 0.248461359;
}

double get_time_stuff(void) {
	return time_stuff;
}
